var main = (function($) {
    var _ = {
        isShow: true,
        /**
         * Settings.
         * @var {object}
         */
        settings: {
            // Preload all images.
            preload: false, // Slide duration (must match "duration.slide" in _vars.scss).
            slideDuration: 500, // Layout duration (must match "duration.layout" in _vars.scss).
            layoutDuration: 750, // Thumbnails per "row" (must match "misc.thumbnails-per-row" in _vars.scss).
            thumbnailsPerRow: 2, // Side of main wrapper (must match "misc.main-side" in _vars.scss).
            mainSide: 'right'
        },
        /**
         * Window.
         * @var {jQuery}
         */
        $window: null,
        /**
         * Body.
         * @var {jQuery}
         */
        $body: null,
        /**
         * Main wrapper.
         * @var {jQuery}
         */
        $main: null,
        /**
         * Thumbnails.
         * @var {jQuery}
         */
        $thumbnails: null,
        /**
         * Viewer.
         * @var {jQuery}
         */
        $viewer: null,
        /**
         * Toggle.
         * @var {jQuery}
         */
        $toggle: null,
        /**
         * Nav (next).
         * @var {jQuery}
         */
        $navNext: null,
        /**
         * Nav (previous).
         * @var {jQuery}
         */
        $navPrevious: null,
        /**
         * Slides.
         * @var {array}
         */
        slides: [],
        /**
         * Current slide index.
         * @var {integer}
         */
        current: null,
        /**
         * Lock state.
         * @var {bool}
         */
        locked: false,
        /**
         * Keyboard shortcuts.
         * @var {object}
         */
        keys: {
            // Escape: Toggle main wrapper.
            27: function() {
                _.toggle();
            }, // Up: Move up.
            38: function() {
                _.up();
            }, // Down: Move down.
            40: function() {
                _.down();
            }, // Space: Next.
            32: function() {
                _.next();
            }, // Right Arrow: Next.
            39: function() {
                _.next();
            }, // Left Arrow: Previous.
            37: function() {
                _.previous();
            }
        },
        /**
         * Initialize properties.
         */
        initProperties: function() {
            // Window, body.
            _.$window = $(window);
            _.$body = $('body');
            // Thumbnails.
            _.$thumbnails = $('#thumbnails');
            // Viewer.
            _.$viewer = $('<div id="viewer">' + '<div class="inner">' + '<div class="nav-next"></div>' + '<div class="nav-previous"></div>' + '<div class="toggle"></div>' + '</div>' + '</div>').appendTo(_.$body);
            // Nav.
            _.$navNext = _.$viewer.find('.nav-next');
            _.$navPrevious = _.$viewer.find('.nav-previous');
            // Main wrapper.
            _.$main = $('#main');
            // Toggle.
            $('<div class="toggle"></div>').appendTo(_.$main);
            _.$toggle = $('.toggle');
            // IE<9: Fix viewer width (no calc support).
            if (skel.vars.IEVersion < 9) _.$window.on('resize', function() {
                window.setTimeout(function() {
                    _.$viewer.css('width', _.$window.width() - _.$main.width());
                }, 100);
            }).trigger('resize');
        },
        /**
         * Initialize events.
         */
        initEvents: function() {
            // Window.
            // Remove is-loading-* classes on load.
            _.$window.on('load', function() {
                _.$body.removeClass('is-loading-0');
                window.setTimeout(function() {
                    _.$body.removeClass('is-loading-1');
                }, 100);
                window.setTimeout(function() {
                    _.$body.removeClass('is-loading-2');
                }, 100 + Math.max(_.settings.layoutDuration - 150, 0));
            });
            // Disable animations/transitions on resize.
            var resizeTimeout;
            _.$window.on('resize', function() {
                _.$body.addClass('is-loading-0');
                window.clearTimeout(resizeTimeout);
                resizeTimeout = window.setTimeout(function() {
                    _.$body.removeClass('is-loading-0');
                }, 100);
            });
            // Viewer.
            // Hide main wrapper on tap (<= medium only).
            _.$viewer.on('touchend', function() {
                if (skel.breakpoint('medium').active) _.hide();
                _.isShow = !_.isShow;
                var $child = _.$viewer.find('.inner,.slide .caption');
                !_.isShow && $child.fadeIn(500) || $child.fadeOut(500);
            });
            // Touch gestures.
            _.$viewer.on('touchstart', function(event) {
                // Record start position.
                _.$viewer.touchPosX = event.originalEvent.touches[0].pageX;
                _.$viewer.touchPosY = event.originalEvent.touches[0].pageY;
            }).on('touchmove', function(event) {
                // No start position recorded? Bail.
                if (_.$viewer.touchPosX === null || _.$viewer.touchPosY === null) return;
                // Calculate stuff.
                var diffX = _.$viewer.touchPosX - event.originalEvent.touches[0].pageX,
                    diffY = _.$viewer.touchPosY - event.originalEvent.touches[0].pageY;
                boundary = 20, delta = 50;
                // Swipe left (next).
                if ((diffY < boundary && diffY > (-1 * boundary)) && (diffX > delta)) _.next();
                // Swipe right (previous).
                else if ((diffY < boundary && diffY > (-1 * boundary)) && (diffX < (-1 * delta))) _.previous();
                // Overscroll fix.
                var th = _.$viewer.outerHeight(),
                    ts = (_.$viewer.get(0).scrollHeight - _.$viewer.scrollTop());
                if ((_.$viewer.scrollTop() <= 0 && diffY < 0) || (ts > (th - 2) && ts < (th + 2) && diffY > 0)) {
                    event.preventDefault();
                    event.stopPropagation();
                }
            });
            // Main.
            // Touch gestures.
            _.$main.on('touchstart', function(event) {
                // Bail on xsmall.
                if (skel.breakpoint('xsmall').active) return;
                // Record start position.
                _.$main.touchPosX = event.originalEvent.touches[0].pageX;
                _.$main.touchPosY = event.originalEvent.touches[0].pageY;
            }).on('touchmove', function(event) {
                // Bail on xsmall.
                if (skel.breakpoint('xsmall').active) return;
                // No start position recorded? Bail.
                if (_.$main.touchPosX === null || _.$main.touchPosY === null) return;
                // Calculate stuff.
                var diffX = _.$main.touchPosX - event.originalEvent.touches[0].pageX,
                    diffY = _.$main.touchPosY - event.originalEvent.touches[0].pageY;
                boundary = 20, delta = 50, result = false;
                // Swipe to close.
                switch (_.settings.mainSide) {
                    case 'left':
                        result = (diffY < boundary && diffY > (-1 * boundary)) && (diffX > delta);
                        break;
                    case 'right':
                        result = (diffY < boundary && diffY > (-1 * boundary)) && (diffX < (-1 * delta));
                        break;
                    default:
                        break;
                }
                if (result) _.hide();
                // Overscroll fix.
                var th = _.$main.outerHeight(),
                    ts = (_.$main.get(0).scrollHeight - _.$main.scrollTop());
                if ((_.$main.scrollTop() <= 0 && diffY < 0) || (ts > (th - 2) && ts < (th + 2) && diffY > 0)) {
                    event.preventDefault();
                    event.stopPropagation();
                }
            });
            // Toggle.
            _.$toggle.on('click', function() {
                _.toggle();
            });
            // Prevent event from bubbling up to "hide event on tap" event.
            _.$toggle.on('touchend', function(event) {
                event.stopPropagation();
            });
            // Nav.
            _.$navNext.on('click', function() {
                _.next();
            });
            _.$navPrevious.on('click', function() {
                _.previous();
            });
            // Keyboard shortcuts.
            // Ignore shortcuts within form elements.
            _.$body.on('keydown', 'input,select,textarea', function(event) {
                event.stopPropagation();
            });
            _.$window.on('keydown', function(event) {
                // Ignore if xsmall is active.
                if (skel.breakpoint('xsmall').active) return;
                // Check keycode.
                if (event.keyCode in _.keys) {
                    // Stop other events.
                    event.stopPropagation();
                    event.preventDefault();
                    // Call shortcut.
                    (_.keys[event.keyCode])();
                }
            });
        },
        /**
         * Initialize viewer.
         */
        initViewer: function() {
            // Bind thumbnail click event.
            _.$thumbnails.on('click', '.thumbnail', function(event) {
                var $this = $(this);
                // Stop other events.
                event.preventDefault();
                event.stopPropagation();
                // Locked? Blur.
                if (_.locked) $this.blur();
                // Switch to this thumbnail's slide.
                _.switchTo($this.data('index'));
            });
            // Create slides from thumbnails.
            _.$thumbnails.children().each(function() {
                var $this = $(this),
                    $thumbnail = $this.children('.thumbnail'),
                    s;
                // Slide object.
                s = {
                    $parent: $this,
                    $slide: null,
                    $slideImage: null,
                    $slideCaption: null,
                    url: $thumbnail.attr('href'),
                    loaded: false
                };
                // Parent.
                $this.attr('tabIndex', '-1');
                // Slide.
                // Create elements.
                s.$slide = $('<div class="slide"><div class="caption"></div><div class="image"></div></div>');
                // Image.
                s.$slideImage = s.$slide.children('.image');
                // Set background stuff.
                s.$slideImage.css('background-image', '').css('background-position', ($thumbnail.data('position') || 'center'));
                // Caption.
                s.$slideCaption = s.$slide.find('.caption');
                // Move everything *except* the thumbnail itself to the caption.
                $this.children().not($thumbnail).appendTo(s.$slideCaption);
                // Preload?
                if (_.settings.preload) {
                    // Force image to download.
                    var $img = $('<img src="' + s.url + '" />');
                    // Set slide's background image to it.
                    s.$slideImage.css('background-image', 'url(' + s.url + ')');
                    // Mark slide as loaded.
                    s.$slide.addClass('loaded');
                    s.loaded = true;
                }
                // Add to slides array.
                _.slides.push(s);
                // Set thumbnail's index.
                $thumbnail.data('index', _.slides.length - 1);
            });
        },
        /**
         * Initialize stuff.
         */
        init: function() {
            // IE<10: Zero out transition delays.
            if (skel.vars.IEVersion < 10) {
                _.settings.slideDuration = 0;
                _.settings.layoutDuration = 0;
            }
            // Skel.
            skel.breakpoints({
                xlarge: '(max-width: 1680px)',
                large: '(max-width: 1280px)',
                medium: '(max-width: 980px)',
                small: '(max-width: 736px)',
                xsmall: '(max-width: 480px)'
            });
            // Everything else.
            _.initProperties();
            _.initViewer();
            _.initEvents();
            // Initial slide.
            window.setTimeout(function() {
                // Show first slide if xsmall isn't active or it just deactivated.
                skel.on('-xsmall !xsmall', function() {
                    if (_.current === null) _.switchTo(0, true);
                });
            }, 0);
        },
        /**
         * Switch to a specific slide.
         * @param {integer} index Index.
         */
        switchTo: function(index, noHide) {
            // Already at index and xsmall isn't active? Bail.
            if (_.current == index && !skel.breakpoint('xsmall').active) return;
            // Locked? Bail.
            if (_.locked) return;
            // Lock.
            _.locked = true;
            // Hide main wrapper if medium is active.
            if (!noHide && skel.breakpoint('medium').active && skel.vars.IEVersion > 8) _.hide();
            // Get slides.
            var oldSlide = (_.current !== null ? _.slides[_.current] : null),
                newSlide = _.slides[index];
            // Update current.
            _.current = index;
            // Deactivate old slide (if there is one).
            if (oldSlide) {
                // Thumbnail.
                oldSlide.$parent.removeClass('active');
                // Slide.
                oldSlide.$slide.removeClass('active');
            }
            // Activate new slide.
            // Thumbnail.
            newSlide.$parent.addClass('active').focus();
            // Slide.
            var f = function() {
                // Old slide exists? Detach it.
                if (oldSlide) oldSlide.$slide.detach();
                // Attach new slide.
                newSlide.$slide.appendTo(_.$viewer);
                // New slide not yet loaded?
                if (!newSlide.loaded) {
                    window.setTimeout(function() {
                        // Mark as loading.
                        newSlide.$slide.addClass('loading');
                        // Wait for it to load.
                        $('<img src="' + newSlide.url + '" />').on('load', function() {
                            //window.setTimeout(function() {
                            // Set background image.
                            newSlide.$slideImage.css('background-image', 'url(' + newSlide.url + ')');
                            // Mark as loaded.
                            newSlide.loaded = true;
                            newSlide.$slide.removeClass('loading');
                            // Mark as active.
                            newSlide.$slide.addClass('active');
                            // Unlock.
                            window.setTimeout(function() {
                                _.locked = false;
                            }, 100);
                            //}, 1000);
                        });
                    }, 100);
                }
                // Otherwise ...
                else {
                    window.setTimeout(function() {
                        // Mark as active.
                        newSlide.$slide.addClass('active');
                        // Unlock.
                        window.setTimeout(function() {
                            _.locked = false;
                        }, 100);
                    }, 100);
                }
            };
            // No old slide? Switch immediately.
            if (!oldSlide)
                (f)();
            // Otherwise, wait for old slide to disappear first.
            else window.setTimeout(f, _.settings.slideDuration);
        },
        /**
         * Switches to the next slide.
         */
        next: function() {
            // Calculate new index.
            var i, c = _.current,
                l = _.slides.length;
            if (c >= l - 1) i = 0;
            else i = c + 1;
            // Switch.
            _.switchTo(i);
        },
        /**
         * Switches to the previous slide.
         */
        previous: function() {
            // Calculate new index.
            var i, c = _.current,
                l = _.slides.length;
            if (c <= 0) i = l - 1;
            else i = c - 1;
            // Switch.
            _.switchTo(i);
        },
        /**
         * Switches to slide "above" current.
         */
        up: function() {
            // Fullscreen? Bail.
            if (_.$body.hasClass('fullscreen')) return;
            // Calculate new index.
            var i, c = _.current,
                l = _.slides.length,
                tpr = _.settings.thumbnailsPerRow;
            if (c <= (tpr - 1)) i = l - (tpr - 1 - c) - 1;
            else i = c - tpr;
            // Switch.
            _.switchTo(i);
        },
        /**
         * Switches to slide "below" current.
         */
        down: function() {
            // Fullscreen? Bail.
            if (_.$body.hasClass('fullscreen')) return;
            // Calculate new index.
            var i, c = _.current,
                l = _.slides.length,
                tpr = _.settings.thumbnailsPerRow;
            if (c >= l - tpr) i = c - l + tpr;
            else i = c + tpr;
            // Switch.
            _.switchTo(i);
        },
        /**
         * Shows the main wrapper.
         */
        show: function() {
            // Already visible? Bail.
            if (!_.$body.hasClass('fullscreen')) return;
            // Show main wrapper.
            _.$body.removeClass('fullscreen');
            // Focus.
            _.$main.focus();
        },
        /**
         * Hides the main wrapper.
         */
        hide: function() {
            // Already hidden? Bail.
            if (_.$body.hasClass('fullscreen')) return;
            // Hide main wrapper.
            _.$body.addClass('fullscreen');
            // Blur.
            _.$main.blur();
        },
        /**
         * Toggles main wrapper.
         */
        toggle: function() {
            if (_.$body.hasClass('fullscreen')) _.show();
            else _.hide();
        },
    };
    return _;
})(jQuery);
main.init();